﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

/// <summary>
/// Summary description for Needlemanwunsch
/// </summary>
public class Needlemanwunsch
{

    //Declarações de matrizes que sumiram com o novo método.
    public char[] mSeqA { get; set; }
    public char[] mSeqB { get; set; }

    public int mScore { get; set; }
    public String mAlignmentSeqA { get; set; }
    public String mAlignmentSeqB { get; set; }

    //Método construtor da classe. O construtor recebe como parâmetro as duas sequências de nucleotídeos.
    public Needlemanwunsch(char[] seqA, char[] seqB)
    {

        //Inicializando os parâmetros
        this.mAlignmentSeqA = "";
        this.mAlignmentSeqB = "";
        this.mSeqA = seqA;
        this.mSeqB = seqB;
        MatrixDAO.addMatrixTable("mD");

        //Loop que inicializa a matriz de score.
        for (int i = 0; i <= this.mSeqA.Length; i++)
        {
            for (int j = 0; j <= this.mSeqB.Length; j++)
            {
                if (i == 0)
                {
                    MatrixDAO.addMatrixTablePosition("mD", i, j, 0 , -j);
                }
                else if (j == 0)
                {
                    MatrixDAO.addMatrixTablePosition("mD", i, j, 0, -i);
                }
                else
                {
                    MatrixDAO.addMatrixTablePosition("mD", i, j, 0, 0);
                }
            }
        }
    }

    //Este método executa o algoritmo nas sequências do objeto.
    public void executar()
    {
        processar();
        backtrack();
    }

    //Este método preenche a matriz de score utilizando as funções de maximização do scoreDiag, scoreLeft e scoreUp.
    public void processar()
    {
        for (int i = 1; i <= this.mSeqA.Length; i++)
        {
            for (int j = 1; j <= this.mSeqB.Length; j++)
            {
                int scoreDiag = Convert.ToInt32(MatrixDAO.getMatrixTablePosition("mD", i - 1, j - 1).score) + weight(i, j); //this.mD[i - 1, j - 1] + weight(i, j);
                int scoreLeft = Convert.ToInt32(MatrixDAO.getMatrixTablePosition("mD", i, j - 1).score) - 1; //this.mD[i, j - 1] - 1;
                int scoreUp = Convert.ToInt32(MatrixDAO.getMatrixTablePosition("mD", i - 1, j).score) - 1; //this.mD[i - 1, j] - 1;
                MatrixDAO.updateMatrixTablePosition("mD",i,j,0, Math.Max(Math.Max(scoreDiag, scoreLeft), scoreUp));
            }
        }
    }

    //Este método calcula e gera o alinhamento de sequências.
    public void backtrack()
    {
        int i = this.mSeqA.Length;
        int j = this.mSeqB.Length;
        this.mScore = Convert.ToInt32(MatrixDAO.getMatrixTablePosition("mD",i,j).score);

        while (i > 0 && j > 0)
        {
            if (MatrixDAO.getMatrixTablePosition("mD", i, j).score == MatrixDAO.getMatrixTablePosition("mD", i - 1, j - 1).score + weight(i, j))
            {
                this.mAlignmentSeqA += this.mSeqA[i - 1];
                this.mAlignmentSeqB += this.mSeqB[j - 1];
                i--;
                j--;
                continue;
            }
            else if (MatrixDAO.getMatrixTablePosition("mD", i, j).score == MatrixDAO.getMatrixTablePosition("mD", i, j - 1).score - 1)
            {
                this.mAlignmentSeqA += "-";
                this.mAlignmentSeqB += this.mSeqB[j - 1];
                j--;
                continue;
            }
            else
            {
                this.mAlignmentSeqA += this.mSeqA[i - 1];
                this.mAlignmentSeqB += "-";
                i--;
                continue;
            }
        }

        char[] mA = this.mAlignmentSeqA.ToCharArray();
        char[] mB = this.mAlignmentSeqB.ToCharArray();
        Array.Reverse(mA);
        Array.Reverse(mB);
        this.mAlignmentSeqA = new String(mA);
        this.mAlignmentSeqB = new String(mB);
    }

    //Este método calcula o match ou mismatch.
    private int weight(int i, int j)
    {
        if (this.mSeqA[i - 1] == this.mSeqB[j - 1])
        {
            return 1;
        }
        else
        {
            return -1;
        }
    }

}